Skip to content

Add Ed25519 challenge-response authentication#296

Open
oetiker wants to merge 2 commits intousetrmnl:mainfrom
oetiker:feature/ed25519-auth
Open

Add Ed25519 challenge-response authentication#296
oetiker wants to merge 2 commits intousetrmnl:mainfrom
oetiker:feature/ed25519-auth

Conversation

@oetiker
Copy link
Copy Markdown
Contributor

@oetiker oetiker commented Jan 29, 2026

Summary

Adds Ed25519 challenge-response authentication as an alternative to API key auth, giving each device a unique cryptographic identity.

Why this matters

API keys are shared secrets — if one is intercepted, an attacker can impersonate the device indefinitely. Ed25519 authentication eliminates this class of attack:

  • No shared secrets on the wire. The private key never leaves the device. Only the public key and a timestamped signature are transmitted.
  • Replay-resistant. Each request is signed with a server-provided timestamp, so captured requests cannot be reused outside the validity window.
  • Tamper-evident identity. The keypair is generated on-device and stored in NVS. A device's public key becomes its unforgeable identity — the server can detect if a device has been cloned or factory-reset.
  • One additional round-trip. The device fetches the server timestamp via /api/time before signing. This ensures clock-independent replay protection.
  • Server-controlled rollout. The server sets auth_mode per device via /api/setup, so existing devices continue working unchanged.

What changed

  • Ed25519 keypair generation and NVS persistence (device_identity.cpp)
  • Challenge-response signing: sign(timestamp_ms || public_key) (auth_signature.cpp)
  • TweetNaCl Ed25519 implementation with hardening: signature malleability rejection (S < L check), VLA stack guard
  • Key generation moved after WiFi connect for RF-based esp_random() entropy
  • API clients send X-Public-Key, X-Signature, X-Timestamp headers when in ed25519 mode
  • Shared bytesToHex extracted to hex_utils.h

Test plan

  • pio run builds cleanly
  • Device generates and persists keypair on first boot
  • X-Public-Key, X-Signature, X-Timestamp headers sent when auth_mode: "ed25519"
  • Factory reset regenerates keypair
  • Fallback to API key auth when server returns auth_mode: "api_key"

🤖 Generated with Claude Code

oetiker and others added 2 commits January 29, 2026 12:16
- Ed25519 keypair generation and NVS storage for device identity
- Challenge-response signing: timestamp_ms || public_key
- Server-controlled auth_mode (api_key or ed25519) via /api/setup
- Reject malleable signatures (S >= L) in crypto_sign_open
- VLA stack guard (TWEETNACL_MAX_MSG_LEN) in detached sign/verify
- Move key generation after WiFi for RF-based esp_random() entropy
- Extract shared bytesToHex into hex_utils.h

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@Fr3dr1ckson
Copy link
Copy Markdown
Collaborator

Hey, thanks for helping improve the firmware! This feature adds quite a bit to review, so it’ll take me some time to dig into it. I’ll post an update once I start working on it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants